[PATCH] QReadWriteLock: fix data race on the d_ptr members
authorDavid Faure <david.faure@kdab.com>
Sat, 5 Apr 2025 11:40:05 +0000 (13:40 +0200)
committerMiao Wang <shankerwangmiao@gmail.com>
Mon, 4 May 2026 08:21:36 +0000 (16:21 +0800)
Testcase: ./tst_qreadwritelock countingTest

WARNING: ThreadSanitizer: data race (pid=356186)
  Read of size 1 at 0x7294000000f8 by thread T12:
    #0 contendedTryLockForRead qtbase/src/corelib/thread/qreadwritelock.cpp:230 (libQt6Core_tsan.so.6+0x6c3743)
    #1 QReadWriteLock::tryLockForRead(QDeadlineTimer) qtbase/src/corelib/thread/qreadwritelock.cpp:190 (libQt6Core_tsan.so.6+0x6c347b)
    #2 QReadWriteLock::lockForRead() qtbase/src/corelib/thread/qreadwritelock.h:68 (tst_qreadwritelock+0xb0f0)
    #3 ReadLockCountThread::run() qtbase/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp:597 (tst_qreadwritelock+0xc506)

  Previous write of size 8 at 0x7294000000f8 by thread T2:
    #0 operator new[](unsigned long, std::align_val_t) <null> (libtsan.so.2+0xa78eb)
    #1 allocate qtbase/src/corelib/tools/qfreelist_p.h:135 (libQt6Core_tsan.so.6+0x6c6079)
    #2 next qtbase/src/corelib/tools/qfreelist_p.h:212 (libQt6Core_tsan.so.6+0x6c5915)
    #3 QReadWriteLockPrivate::allocate() qtbase/src/corelib/thread/qreadwritelock.cpp:564 (libQt6Core_tsan.so.6+0x6c5354)
    #4 contendedTryLockForRead qtbase/src/corelib/thread/qreadwritelock.cpp:218 (libQt6Core_tsan.so.6+0x6c364f)

The loadRelaxed() at the beginning of tryLockForRead/tryLockForWrite
isn't enough to bring us the non-atomic write of the recursive bool.
Same issue with the std::mutex itself.

Pick-to: 6.9 6.8 6.5
Change-Id: I6f5e371cf94292b643cb36041d1406b19d22cdbe
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Gbp-Pq: Name upstream_qreadwritelock_data_race.patch

src/corelib/thread/qreadwritelock.cpp

index e79bed223117308e5dc5d15b7a026cc8fd7dca1a..0fefc7dab693294e28e9524f625f36f0893fd10b 100644 (file)
@@ -225,7 +225,10 @@ Q_NEVER_INLINE static bool contendedTryLockForRead(QAtomicPointer<QReadWriteLock
             d = val;
         }
         Q_ASSERT(!isUncontendedLocked(d));
-        // d is an actual pointer;
+        // d is an actual pointer; acquire its contents
+        d = d_ptr.loadAcquire();
+        if (!d || isUncontendedLocked(d))
+            continue;
 
         if (d->recursive)
             return d->recursiveLockForRead(timeout);
@@ -333,7 +336,10 @@ Q_NEVER_INLINE static bool contendedTryLockForWrite(QAtomicPointer<QReadWriteLoc
             d = val;
         }
         Q_ASSERT(!isUncontendedLocked(d));
-        // d is an actual pointer;
+        // d is an actual pointer; acquire its contents
+        d = d_ptr.loadAcquire();
+        if (!d || isUncontendedLocked(d))
+            continue;
 
         if (d->recursive)
             return d->recursiveLockForWrite(timeout);